package com.cai310.lottery.service.lottery.jclq.impl;

import java.math.BigDecimal;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.cai310.lottery.JclqConstant;
import com.cai310.lottery.common.Lottery;
import com.cai310.lottery.common.SalesMode;
import com.cai310.lottery.common.SchemeState;
import com.cai310.lottery.common.WinningUpdateStatus;
import com.cai310.lottery.dto.lottery.jclq.JclqSchemeDTO;
import com.cai310.lottery.entity.lottery.Period;
import com.cai310.lottery.entity.lottery.PrintInterface;
import com.cai310.lottery.entity.lottery.jclq.JclqMatch;
import com.cai310.lottery.entity.lottery.jclq.JclqScheme;
import com.cai310.lottery.entity.lottery.jclq.JclqSchemeMatch;
import com.cai310.lottery.entity.lottery.jclq.JclqMatch;
import com.cai310.lottery.entity.lottery.ticket.Ticket;
import com.cai310.lottery.exception.DataException;
import com.cai310.lottery.service.ServiceException;
import com.cai310.lottery.service.lottery.PeriodEntityManager;
import com.cai310.lottery.service.lottery.PrintEntityManager;
import com.cai310.lottery.service.lottery.SchemeEntityManager;
import com.cai310.lottery.service.lottery.dczc.MatchNotEndException;
import com.cai310.lottery.service.lottery.impl.SchemeServiceImpl;
import com.cai310.lottery.service.lottery.jclq.JclqMatchEntityManager;
import com.cai310.lottery.service.lottery.jclq.JclqSchemeService;
import com.cai310.lottery.service.lottery.ticket.impl.TicketEntityManager;
import com.cai310.lottery.support.Item;
import com.cai310.lottery.support.JcWonMatchItem;
import com.cai310.lottery.support.PrintWonItem;
import com.cai310.lottery.support.PrizeWork;
import com.cai310.lottery.support.jclq.JclqCompoundContent;
import com.cai310.lottery.support.jclq.JclqMatchItem;
import com.cai310.lottery.support.jclq.JclqMultiplePassPrizeWork;
import com.cai310.lottery.support.jclq.JclqSimplePrizeWork;
import com.cai310.lottery.support.jclq.PassType;
import com.cai310.lottery.support.jclq.PlayType;
import com.cai310.lottery.support.jclq.TicketItem;
import com.cai310.lottery.support.jclq.JclqCompoundContent;
import com.cai310.lottery.support.jclq.JclqMatchItem;
import com.cai310.lottery.ticket.protocol.local.utils.EL11TO5CPUtil;
import com.cai310.lottery.ticket.protocol.local.utils.JCLQCPUtil;
import com.cai310.lottery.ticket.protocol.local.utils.JCZQCPUtil;
import com.cai310.lottery.ticket.protocol.local.utils.QueryPVisitor;
import com.cai310.lottery.utils.BigDecimalUtil;
import com.cai310.utils.JsonUtil;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

@Service("jclqSchemeServiceImpl")
@Transactional
public class JclqSchemeServiceImpl extends SchemeServiceImpl<JclqScheme, JclqSchemeDTO> implements JclqSchemeService {

	@Autowired
	private JclqSchemeEntityManagerImpl schemeManager;

	@Autowired
	private JclqMatchEntityManager matchEntityManager;

	@Resource
	protected PeriodEntityManager periodManager;
	
	@Resource
	protected TicketEntityManager ticketEntityManager;
	
	@Resource
	protected PrintEntityManager printEntityManager;

	@Override
	protected SchemeEntityManager<JclqScheme> getSchemeEntityManager() {
		return schemeManager;
	}

	public void updatePrize(long schemeId) {
		updateResult(schemeId);
	}

	public void updateResult(long schemeId) {
		JclqScheme scheme = schemeManager.getScheme(schemeId);
		if (scheme == null)
			throw new ServiceException("方案不存在.");
		if (scheme.isUpdateWon())
			throw new ServiceException("方案[" + scheme.getSchemeNumber() + "]已更新中奖,不能再更新中奖.");
		else if (scheme.isUpdatePrize())
			throw new ServiceException("方案[" + scheme.getSchemeNumber() + "]已更新奖金,不能再更新中奖.");
		else if (scheme.isPrizeSended())
			throw new ServiceException("方案[" + scheme.getSchemeNumber() + "]已派奖,不能再更新中奖.");

		JclqCompoundContent compoundConent = scheme.getCompoundContent();
		List<JclqMatchItem> itemList = compoundConent.getItems();

		List<String> matchKeys = Lists.newArrayList();
		for (JclqMatchItem item : itemList) {
			matchKeys.add(item.getMatchKey());
		}

		List<JclqMatch> mathList = matchEntityManager.findMatchs(matchKeys);
		Map<String, JclqMatch> drawdMatchMap = Maps.newHashMap();
		for (JclqMatch match : mathList) {
			String matchKey = match.getMatchKey();
			if (!match.isEnded() && !match.isCancel())
				throw new ServiceException("[" + matchKey + "]未结束.");
			Item rs = null;
			if(PlayType.MIX.equals(scheme.getPlayType())){
				for (JclqMatchItem matchItem : itemList) {
					if(matchItem.getMatchKey().equals(match.getMatchKey())){
						 rs = match.getResult(matchItem.getPlayType());	
					}
				}
			}else{
				 rs = match.getSingleResult(scheme.getPlayType());
			}
			if (!match.isCancel() && rs == null)
				throw new ServiceException("[" + matchKey + "]未填开奖结果.");
			drawdMatchMap.put(matchKey, match);
		}

		PrizeWork prizeWork;
		List<JcWonMatchItem> correctList = Lists.newArrayList();
		List<PassType> passTypeList = scheme.getPassTypeList();
		switch (scheme.getPassMode()) {
		case SINGLE:
			for (JclqMatchItem matchItem : itemList) {
				String matchKey = matchItem.getMatchKey();

				JcWonMatchItem wonItem = new JcWonMatchItem();
				wonItem.setMatchKey(matchKey);
				wonItem.setSelectCount(matchItem.selectedCount());

				JclqMatch match = drawdMatchMap.get(matchKey);
				boolean won = false;
				if (match.isCancel()) {
					wonItem.setCancel(true);
					won = true;
				} else if (match.isEnded()) {
					Item rs = match.getSingleResult(scheme.getPlayType());
					if (matchItem.hasSelect(rs)) {
						wonItem.setResultItem(rs);
						if(PlayType.SFC.equals(scheme.getPlayType())){
							List<Map<String, Map<String, Double>>> printAwardList = null;
							try {
								printAwardList = scheme.getPrintAwardList();
								if (printAwardList.size() != 1)
										throw new ServiceException("出票SP值数据异常.");
								Map<String, Map<String, Double>> matchPrintAwardMap = null;
								if (printAwardList != null && printAwardList.size() == 1) {
									matchPrintAwardMap = printAwardList.get(0);
									Map<String, Double> printAwardMap = matchPrintAwardMap.get(matchKey);
									Object printAwardObj  = printAwardMap.get(rs.getValue());
									if (printAwardObj == null )
											throw new MatchNotEndException("出票SP值未更新.");
									Double printAward = Double.valueOf(""+printAwardObj);
									if (printAward == null || printAward <= 0)
											throw new MatchNotEndException("出票SP值未更新.");
	
									wonItem.setAward(printAward*2);
								}
							} catch (Exception e) {
								Double resultAward = match.getResultSp(scheme.getPlayType());
								if (resultAward == null || resultAward == 0)
									throw new MatchNotEndException("[#" + match.getId() + "][" + scheme.getPlayType().getText()
											+ "]最终SP值未更新.");
								wonItem.setAward(resultAward);
							}
						}else{
							Double resultAward = match.getResultSp(scheme.getPlayType());
							if (resultAward == null || resultAward == 0)
								throw new MatchNotEndException("[#" + match.getId() + "][" + scheme.getPlayType().getText()
										+ "]最终SP值未更新.");
							wonItem.setAward(resultAward);
						}
						won = true;
					}
				}
				if (won)
					correctList.add(wonItem);
			}

			if (passTypeList.size() > 1)
				throw new ServiceException("过关类型异常.");

			prizeWork = new JclqSimplePrizeWork(scheme.getPassMode(), scheme.getMultiple(), scheme.getPassTypeList()
					.get(0), correctList);
			break;
		case PASS:
		case MIX_PASS:
			if (scheme.getState() == SchemeState.CANCEL || scheme.getState() == SchemeState.REFUNDMENT) {
				prizeWork = null;
			} else {
				if (!scheme.isRealPrinted())
					throw new MatchNotEndException("出票SP值未更新.");

				List<TicketItem> ticketList;
				List<Map<String, Map<String, Double>>> printAwardList;
				try {
					ticketList = scheme.getTicketList();
					printAwardList = scheme.getPrintAwardList();
				} catch (DataException e) {
					throw new ServiceException(e.getMessage(), e);
				}
				if (printAwardList.size() != ticketList.size())
					throw new ServiceException("出票SP值数据异常.");

				if (ticketList.size() > 1) {
					for (Map<String, Map<String, Double>> map : printAwardList) {
						if (map == null)
							throw new MatchNotEndException("出票SP值未更新.");
					}

					prizeWork = new JclqMultiplePassPrizeWork(scheme.getPlayType(), scheme.getPassMode(), itemList,
							ticketList, printAwardList, drawdMatchMap);
				} else {
					Map<String, Map<String, Double>> matchPrintAwardMap = null;
					if (printAwardList != null && printAwardList.size() == 1) {
						matchPrintAwardMap = printAwardList.get(0);
					} else {
						throw new MatchNotEndException("出票SP值未更新.");
					}

					for (int i = 0; i < itemList.size(); i++) {
						JclqMatchItem matchItem = itemList.get(i);
						String matchKey = matchItem.getMatchKey();

						JcWonMatchItem wonItem = new JcWonMatchItem();
						wonItem.setMatchKey(matchKey);
						wonItem.setSelectCount(matchItem.selectedCount());

						JclqMatch match = drawdMatchMap.get(matchKey);
						boolean won = false;
						if (match.isCancel()) {
							wonItem.setAward(1d);
							wonItem.setCancel(true);
							wonItem.setAward(1d);
							won = true;
						} else if (match.isEnded()) {
							Map<String, Double> printAwardMap = matchPrintAwardMap.get(matchKey);
							Double referenceValue = Double.valueOf(""+printAwardMap.get(JclqConstant.REFERENCE_VALUE_KEY));
							Item rs = null;
							if(PlayType.MIX.equals(scheme.getPlayType())){
								 rs = match.getPassResult(matchItem.getPlayType(), referenceValue);	
							}else{
								 rs = match.getPassResult(scheme.getPlayType(), referenceValue);
							}
							if (matchItem.hasSelect(rs)) {
								Double printAward = Double.valueOf(""+printAwardMap.get(rs.getValue()));
								if (printAward == null || printAward <= 0)
									throw new MatchNotEndException("出票SP值未更新.");

								wonItem.setAward(printAward);
								wonItem.setResultItem(rs);
								won = true;
							}
						}
						if (won)
							correctList.add(wonItem);
					}

					if (passTypeList.size() > 1)
						throw new ServiceException("过关类型异常.");

					prizeWork = new JclqSimplePrizeWork(scheme.getPassMode(), scheme.getMultiple(), scheme
							.getPassTypeList().get(0), correctList);
				}
			}
			break;
		default:
			throw new ServiceException("过关模式不合法.");
		}

		if (prizeWork != null && prizeWork.isWon()) {
			try {
				scheme.doUpdateResult(prizeWork.getWonDetail().toString());
				scheme.doUpdatePrize(BigDecimal.valueOf(prizeWork.getTotalPrize()),
						BigDecimal.valueOf(prizeWork.getTotalPrizeAfterTax()), prizeWork.getPrizeDetail().toString());
			} catch (DataException e) {
				throw new ServiceException(e.getMessage());
			}
		} else {
			scheme.setWinningUpdateStatus(WinningUpdateStatus.PRICE_UPDATED);
			scheme.setWon(false);
			scheme.setWonDetail("");
			scheme.setPrize(BigDecimal.ZERO);
			scheme.setPrizeAfterTax(BigDecimal.ZERO);
			scheme.setPrizeDetail("");
		}
		scheme = getSchemeEntityManager().saveScheme(scheme);
	}

	@Override
	protected JclqScheme newSchemeInstance(JclqSchemeDTO schemeDTO) {
		JclqScheme scheme = super.newSchemeInstance(schemeDTO);

		scheme.setPlayType(schemeDTO.getPlayType());
		scheme.setSchemeType(schemeDTO.getSchemeType());
		scheme.setPassMode(schemeDTO.getPassMode());
		scheme.setTicketContent(schemeDTO.getTicketContent());

		long passTypeValue = 0;
		for (PassType passType : schemeDTO.getPassTypes()) {
			passTypeValue |= passType.getValue();
		}
		scheme.setPassType(passTypeValue);

		return scheme;
	}

	@Override
	public JclqScheme createScheme(JclqSchemeDTO schemeDTO) {
		JclqScheme scheme = super.createScheme(schemeDTO);

		if (scheme.getMode() == SalesMode.COMPOUND) {
			JclqCompoundContent compoundContent = scheme.getCompoundContent();
			if (compoundContent != null) {
				for (JclqMatchItem matchItem : compoundContent.getItems()) {
					JclqSchemeMatch schemeMatch = new JclqSchemeMatch();
					schemeMatch.setMatchKey(matchItem.getMatchKey());
					schemeMatch.setSchemeId(scheme.getId());
					schemeManager.saveSchemeMatch(schemeMatch);
				}
			}
		}

		return scheme;
	}

	@Override
	protected void checkConformPeriodSubscriptionConfig(JclqScheme scheme) {
		super.checkConformPeriodSubscriptionConfig(scheme);

		List<JclqMatch> matchList = matchEntityManager.findMatchsOfUnEnd();
		List<String> keyList = Lists.newArrayList();
		if (matchList != null && !matchList.isEmpty()) {
			for (JclqMatch m : matchList) {
				keyList.add(m.getMatchKey());
			}
		}

		switch (scheme.getMode()) {
		case COMPOUND:
			JclqCompoundContent content = scheme.getCompoundContent();
			for (JclqMatchItem item : content.getItems()) {
				if (!keyList.contains(item.getMatchKey()))
					throw new ServiceException("第" + (item.getMatchKey()) + "场赛事已经截止销售.");
			}
			break;
		case SINGLE:
			throw new ServiceException("目前暂不支持单式.");
		default:
			throw new ServiceException("投注方式不正确.");
		}
	}

	@Override
	public boolean isSaleEnded(Long schemeId) {
		boolean isSaleEnded = super.isSaleEnded(schemeId);

		if (!isSaleEnded) {
			List<JclqMatch> matchList = matchEntityManager.findMatchsOfUnEnd();
			List<String> keyList = Lists.newArrayList();
			if (matchList != null && !matchList.isEmpty()) {
				for (JclqMatch m : matchList) {
					keyList.add(m.getMatchKey());
				}
			}
			JclqScheme scheme = getSchemeEntityManager().getScheme(schemeId);
			switch (scheme.getMode()) {
			case COMPOUND:
				JclqCompoundContent content = scheme.getCompoundContent();
				for (JclqMatchItem item : content.getItems()) {
					if (!keyList.contains(item.getMatchKey())) {
						isSaleEnded = true;
						break;
					}
				}
				break;
			case SINGLE:
				throw new ServiceException("目前暂不支持单式.");
			default:
				throw new ServiceException("投注方式不正确.");
			}
		}
		return isSaleEnded;
	}

	@SuppressWarnings("unchecked")
	@Override
	protected void checkConformPeriodInitConfig(Period period, JclqSchemeDTO schemeDTO) {
		super.checkConformPeriodInitConfig(period, schemeDTO);

		List<JclqMatch> matchList = matchEntityManager.findMatchsOfUnEnd();
		Map<String,JclqMatch> keyMap = Maps.newHashMap();
		if (matchList != null && !matchList.isEmpty()) {
			for (JclqMatch m : matchList) {
				keyMap.put(m.getMatchKey(),m);
			}
		}
		Date endTime = null;
		switch (schemeDTO.getMode()) {
		case COMPOUND:
			@SuppressWarnings("rawtypes")
			Map classMap = new HashMap();
			classMap.put("items", JclqMatchItem.class);
			JclqCompoundContent content = JsonUtil.getObject4JsonString(schemeDTO.getContent(),
					JclqCompoundContent.class, classMap);
			JclqMatch jclqMatch = null;
			for (JclqMatchItem item : content.getItems()) {
				if (!keyMap.containsKey(item.getMatchKey()))
					throw new ServiceException("第" + (item.getMatchKey()) + "场赛事已经截止销售.");
				jclqMatch = keyMap.get(item.getMatchKey());
				try {
					if (endTime == null || endTime.after(jclqMatch.getTicketOfficialEndTime()))
						endTime = jclqMatch.getTicketOfficialEndTime();
				} catch (DataException e) {
					throw new ServiceException("获取比赛场次时间错误.");
				}
			}
			schemeDTO.setOfficialEndTime(endTime);
			break;
		case SINGLE:
			throw new ServiceException("目前暂不支持单式.");
		default:
			throw new ServiceException("投注方式不正确.");
		}
	}
	public boolean handleTransaction(JclqScheme scheme,List<String> endLineIds) {
		boolean anyEnded = false;// 方案是否包含一场已结束的赛事标识
		List<String> matchKeys = scheme.getSelectedMatchKeys();
		for (String endLineId : endLineIds) {
			if (matchKeys.contains(endLineId)) {
				anyEnded = true;
				break;
			}
		}
		if (anyEnded) {
			commitOrCancelTransaction(scheme.getId());
			return true;
		}

		return false;
	}
	@Override
	public Lottery getLotteryType() {
		return Lottery.JCLQ;
	}
}
